#include <stdint.h>
#include "gu_jimgutil_CsCvtNativeImpl.h"
#include "jni_utilits.h"
#include "sample_log.h"
#include "rotate.h"

using namespace gdface;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
	jni_utilits::setJVM(vm);
	SAMPLE_OUT("jimgutil jni loaded");
	return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
	SAMPLE_OUT("jimgutil jni unloaded");
}
JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_RGBA2BGR
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint stride, jbyteArray output){
	if (input == nullptr || output == nullptr) {
		jni_utilits::throwNullPointerException();
		return ;
	}
	auto input_len = env->GetArrayLength(input);
	if (stride < width*4 || input_len != stride * height) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return ;
	}
	auto output_len = env->GetArrayLength(output);
	if (output_len != width * height * 3) {
		jni_utilits::throwIllegalArgumentException("INVALID OUTPUT SIZE");
		return ;
	}
	auto raii_input  = jni_utilits::raii_GetByteArrayElements(input);
	auto raii_output = jni_utilits::raii_GetByteArrayElements(output,0);
	auto input_line_ptr = raii_input.get();
	auto output_line_ptr = raii_output.get();

	for (int y = 0; y < height; ++y) {
		for (int x = 0, p = 0; x < width * 4; x += 4, p += 3) {
			output_line_ptr[ p     ] = input_line_ptr[x + 2 ] ; // BLUE
			output_line_ptr[ p + 1 ] = input_line_ptr[x + 1 ] ; // GREEN
			output_line_ptr[ p + 2 ] = input_line_ptr[x     ] ; // RED
		}
		input_line_ptr  += stride;
		output_line_ptr += width * 3;
	}
	return ;
}
JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_RGBA2RGB
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint stride, jbyteArray output) {
	if (input == nullptr || output == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (stride < width * 4 || input_len != stride * height) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	auto output_len = env->GetArrayLength(output);
	if (output_len != width * height * 3) {
		jni_utilits::throwIllegalArgumentException("INVALID OUTPUT SIZE");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input);
	auto raii_output = jni_utilits::raii_GetByteArrayElements(output, 0);
	auto input_line_ptr = raii_input.get();
	auto output_line_ptr = raii_output.get();

	for (int y = 0; y < height; ++y) {
		for (int x = 0, p = 0; x < width * 4; x += 4, p += 3) {
			output_line_ptr[p    ] = input_line_ptr[x    ]; // RED
			output_line_ptr[p + 1] = input_line_ptr[x + 1]; // GREEN
			output_line_ptr[p + 2] = input_line_ptr[x + 2]; // BLUE
		}
		input_line_ptr += stride;
		output_line_ptr += width * 3;
	}
	return;
}
JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_RGBA2GRAY
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint stride, jbyteArray output) {
	if (input == nullptr || output == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (stride < width * 4 || input_len != stride * height) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	auto output_len = env->GetArrayLength(output);
	if (output_len != width * height) {
		jni_utilits::throwIllegalArgumentException("INVALID OUTPUT SIZE");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input);
	auto raii_output = jni_utilits::raii_GetByteArrayElements(output, 0);
	auto input_line_ptr = raii_input.get();
	auto output_line_ptr = raii_output.get();

	for (int y = 0; y < height; ++y) {
		for (int x = 0,p=0; x < width; ++x, p += 4) {
			uint32_t R = input_line_ptr[p     ];  // RED
			uint32_t G = input_line_ptr[p + 1 ];  // GREEN
			uint32_t B = input_line_ptr[p + 2 ];  // BLUE
			output_line_ptr[x] = (jbyte)((R * 76 + G * 150 + B * 30) >> 8);
		}
		input_line_ptr += stride;
		output_line_ptr += width;
	}
	return;
}
JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_RGB2BGR
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint stride, jbyteArray output) {
	if (input == nullptr || output == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (stride < width*3 || input_len != stride * height) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	auto output_len = env->GetArrayLength(output);
	if (output_len != width * height * 3) {
		jni_utilits::throwIllegalArgumentException("INVALID OUTPUT SIZE");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input);
	auto raii_output = jni_utilits::raii_GetByteArrayElements(output,0);
	auto input_line_ptr = raii_input.get();
	auto output_line_ptr = raii_output.get();

	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width * 3; x += 3) {
			output_line_ptr[x    ] = input_line_ptr[x + 2 ] ; // BLUE
			output_line_ptr[x + 1] = input_line_ptr[x + 1 ] ; // GREEN
			output_line_ptr[x + 2] = input_line_ptr[x     ] ; // RED
		}
		input_line_ptr  += stride;
		output_line_ptr += width * 3;
	}
	return;
}
JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_swap3byte
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint stride) {
	if (input == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (stride < width * 3 || input_len != stride * height) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input,0);
	auto input_line_ptr = raii_input.get();

	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width * 3; x += 3) {
			std::swap(input_line_ptr[x], input_line_ptr[x + 2]);
		}
		input_line_ptr += stride;
	}
	return;
}

JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_RGB2GRAY
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint stride, jbyteArray output) {
	if (input == nullptr || output == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (stride < width * 3 || input_len != stride * height) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	auto output_len = env->GetArrayLength(output);
	if (output_len != width * height ) {
		jni_utilits::throwIllegalArgumentException("INVALID OUTPUT SIZE");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input);
	auto raii_output = jni_utilits::raii_GetByteArrayElements(output, 0);
	auto input_line_ptr = raii_input.get();
	auto output_line_ptr = raii_output.get();

	for (int y = 0; y < height; ++y) {
		for (int x = 0, p = 0; x < width; ++x, p += 3) {
			uint32_t R = input_line_ptr[p    ];  // RED
			uint32_t G = input_line_ptr[p + 1];  // GREEN
			uint32_t B = input_line_ptr[p + 2];  // BLUE
			output_line_ptr[x] = (jbyte)((R * 76 + G * 150 + B * 30) >> 8);
		}
		input_line_ptr  += stride;
		output_line_ptr += width;
	}
	return;
}

JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_BGR2RGBA
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint stride, jbyteArray output) {
	if (input == nullptr || output == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (stride < width * 3 || input_len != stride * height) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	auto output_len = env->GetArrayLength(output);
	if (output_len != width * height * 4) {
		jni_utilits::throwIllegalArgumentException("INVALID OUTPUT SIZE");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input);
	auto raii_output = jni_utilits::raii_GetByteArrayElements(output, 0);
	auto input_line_ptr = raii_input.get();
	auto output_line_ptr = raii_output.get();

	for (int y = 0; y < height; ++y) {
		for (int x = 0, p = 0; x < width * 3; x += 3, p += 4) {
			output_line_ptr[p    ] = input_line_ptr[x + 2]; // RED
			output_line_ptr[p + 1] = input_line_ptr[x + 1]; // GREEN
			output_line_ptr[p + 2] = input_line_ptr[x    ]; // BLUE
			output_line_ptr[p + 3] = 0;
		}
		input_line_ptr  += stride;
		output_line_ptr += width * 4;
	}
	return;
}
JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_RGB2RGBA
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint stride, jbyteArray output) {
	if (input == nullptr || output == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (stride < width * 3 || input_len != stride * height) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	auto output_len = env->GetArrayLength(output);
	if (output_len != width * height * 4) {
		jni_utilits::throwIllegalArgumentException("INVALID OUTPUT SIZE");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input);
	auto raii_output = jni_utilits::raii_GetByteArrayElements(output, 0);
	auto input_line_ptr = raii_input.get();
	auto output_line_ptr = raii_output.get();

	for (int y = 0; y < height; ++y) {
		for (int x = 0, p = 0; x < width * 3; x += 3, p += 4) {
			output_line_ptr[p    ] = input_line_ptr[x    ]; // BLUE
			output_line_ptr[p + 1] = input_line_ptr[x + 1]; // GREEN
			output_line_ptr[p + 2] = input_line_ptr[x + 2]; // RED
			output_line_ptr[p + 3] = 0;
		}
		input_line_ptr  += stride;
		output_line_ptr += width * 4;
	}
	return;
}


JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_rotate
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint bpp, jint angle) {
	if (input == nullptr ) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (input_len != width * height * bpp) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	if (bpp < 1 || bpp > 4) {
		jni_utilits::throwIllegalArgumentException("INVALID bpp,available value:1,2,3,4");
		return;
	}
	if (0 == angle) {
		return;
	}
	if (90 != angle && 180 != angle && 270 != angle) {
		jni_utilits::throwIllegalArgumentException("INVALID angle,available value:0,90,180,270");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input,0);

	jimgutil::rotate(raii_input.get(), width, height, bpp, angle);
}
JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_hflip
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint bpp) {
	if (input == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (input_len != width * height * bpp) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	if (bpp < 1 || bpp > 4) {
		jni_utilits::throwIllegalArgumentException("INVALID bpp,available value:1,2,3,4");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input,0);
	jimgutil::hflip(raii_input.get(), width, height, bpp);
}
JNIEXPORT void JNICALL Java_gu_jimgutil_CsCvtNativeImpl_vflip
(JNIEnv * env, jclass, jbyteArray input, jint width, jint height, jint bpp) {
	if (input == nullptr) {
		jni_utilits::throwNullPointerException();
		return;
	}
	auto input_len = env->GetArrayLength(input);
	if (input_len != width * height * bpp) {
		jni_utilits::throwIllegalArgumentException("INVALID INPUT SIZE");
		return;
	}
	if (bpp < 1 || bpp > 4) {
		jni_utilits::throwIllegalArgumentException("INVALID bpp,available value:1,2,3,4");
		return;
	}
	auto raii_input = jni_utilits::raii_GetByteArrayElements(input,0);
	jimgutil::vflip(raii_input.get(), width, height, bpp);
}
